package com.starsoft.quant.service;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.math3.stat.regression.SimpleRegression;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.starsoft.frame.util.DateUtil;
import com.starsoft.quant.analysis.VolatilityService;
import com.starsoft.smdc.bean.SmdcMarketDaily;
import com.starsoft.smdc.bean.SmdcSecurity;
import com.starsoft.smdc.service.HolidayService;
import com.starsoft.smdc.service.MarketDailyService;

@Component
public class AnalysisService {

	@Autowired
	MarketDailyService marketService;
	
	@Autowired
	HolidayService holidayService;
	
	@Autowired
	VolatilityService volatilityService;

	public Map<String, Object> getRisk(SmdcSecurity sec, Date beginDate, Date endDate) {
		List<SmdcMarketDaily> markets = marketService.getBetweenAnd(sec.getSecId(), beginDate, endDate);
		SmdcMarketDaily lastMarket = markets.get(markets.size()-1);
		SmdcMarketDaily firstMarket = markets.get(0);
		double profit = lastMarket.getClosePrice() * lastMarket.getAdjFactor() / firstMarket.getClosePrice()
				/ firstMarket.getAdjFactor() - 1;
		double profitNav = 0;
		if (lastMarket.getDivi() != null && firstMarket.getDivi() != null) {
			profitNav = lastMarket.getDivi() / firstMarket.getDivi() - 1;
		} else if (lastMarket.getSecNav() != null && firstMarket.getSecNav() != null) {
			profitNav = lastMarket.getSecNav() * lastMarket.getAdjFactor() / firstMarket.getSecNav()
					/ firstMarket.getAdjFactor() - 1;
		}
		
		Map<String, Object> result = new HashMap<>();
		result.put("secId", sec.getSecId());
		result.put("secName", sec.getSecName());
		result.put("time", DateUtil.getYears(beginDate, endDate));
		result.put("volatility", volatilityService.getVolatility(markets));
		result.put("profit", profit);
		result.put("profitNav", profitNav);
		result.put("maxDown", getMaxDown(markets));
		
		result.put("profitThisYear", getProfitYear(sec, 0));
		result.put("profitLastYear", getProfitYear(sec, 1));
		result.put("profitBeforeLastYear", getProfitYear(sec, 2));
		
		return result;
	}
	
	public Map<String, Object> getRegression(SmdcSecurity sec, SmdcSecurity secBase, List<Date> timeLine) {
		List<SmdcMarketDaily> marketSecA = new ArrayList<>();
		List<SmdcMarketDaily> marketSecBase = new ArrayList<>();
		SimpleRegression regression = new SimpleRegression();
		for(Date date : timeLine){
			SmdcMarketDaily marketA = marketService.getMarketDaily(sec, date);
			SmdcMarketDaily marketBase = marketService.getMarketDaily(secBase, date);
			if(marketA!=null && marketBase!=null) {
				marketSecA.add(marketA);
				marketSecBase.add(marketBase);
				regression.addData(marketA.getPercent(), marketBase.getPercent());
			}
		}
		
		Map<String, Object> result = new HashMap<>();
		result.put("correlation", regression.getR());
		result.put("intercept", regression.getIntercept() * 250);
		result.put("slope", regression.getSlope());
		
		result.put("secId", sec.getSecId());
		result.put("secName", sec.getSecName());
		result.put("secBaseId", secBase.getSecId());
		result.put("secBaseName", secBase.getSecName());
		
		result.put("volatility", volatilityService.getVolatility(marketSecA));
		result.put("baseVolatility", volatilityService.getVolatility(marketSecBase));
		
		return result;
	}
	
	public List<Double> getProfitLine(SmdcSecurity sec, List<Date> timeLine) {
		List<Double> result = new ArrayList<>();
		for(Date date : timeLine){
			SmdcMarketDaily market = marketService.getMarketDaily(sec, date);
			if(market!=null) {
				result.add(market.getClosePrice()*market.getAdjFactor());
			} else {
				result.add(null);
			}
		}
		return volatilityService.rebase(result);
	}
	
	public List<Double> getProfitLineNav(SmdcSecurity sec, List<Date> timeLine) {
		List<Double> result = new ArrayList<>();
		for(Date date : timeLine){
			SmdcMarketDaily market = marketService.getMarketDaily(sec, date);
			if(market!=null) {
				result.add(market.getSecNav()*market.getAdjFactor());
			} else {
				result.add(null);
			}
		}
		return volatilityService.rebase(result);
	}

	public List<Double> getProfitFullEarning(SmdcSecurity sec, List<Date> timeLine) {
		List<Double> result = new ArrayList<>();
		for(Date date : timeLine){
			SmdcMarketDaily market = marketService.getMarketDaily(sec, date);
			if(market!=null) {
				if(market.getDivi()==null) return null;
				result.add(market.getDivi());
			} else {
				result.add(null);
			}
		}
		return volatilityService.rebase(result);
	}
	
	public static double getMaxDown(List<SmdcMarketDaily> markets) {
		double maxValue = 0;
		double maxDown = 0;
		for (int i = 0; i < markets.size(); i++) {
			double value = markets.get(i).getClosePrice() * markets.get(i).getAdjFactor();
			if(markets.get(i).getDivi()!=null) value = markets.get(i).getDivi();
			if (value > maxValue) {
				maxValue = value;
			} else if (value < maxValue) {
				double thisDown = value / maxValue - 1;
				if (thisDown < maxDown) {
					maxDown = thisDown;
				}
			}
		}
		return maxDown;
	}
	
	
	public Double getProfitYear(SmdcSecurity sec, int yearNum) {
		Date beginDate = null, endDate = null;
		int year = LocalDate.now().getYear()-yearNum;
		beginDate = DateUtil.toDate(year+"0101", "yyyyMMdd");
		beginDate = holidayService.getBusinessDay(beginDate, true);
		if (yearNum == 0) {
			endDate = holidayService.getNextBusinessDay(new Date(), -1);
		}else {
			endDate = DateUtil.toDate(year+"1231", "yyyyMMdd");
			endDate = holidayService.getBusinessDay(endDate, true);
		}
		
		SmdcMarketDaily beginMarket =  marketService.getMarketDaily(sec, beginDate);
		SmdcMarketDaily endMarket = marketService.getMarketDaily(sec, endDate);
		
		if(beginMarket==null ||endMarket==null) return null;
		
		double profit = endMarket.getClosePrice() * endMarket.getAdjFactor() / beginMarket.getClosePrice()
				/ beginMarket.getAdjFactor() - 1;
		if (endMarket.getDivi() != null && beginMarket.getDivi() != null) {
			profit = endMarket.getDivi() / beginMarket.getDivi() - 1;
		} else if (endMarket.getSecNav() != null && beginMarket.getSecNav() != null) {
			profit = endMarket.getSecNav() * endMarket.getAdjFactor() / beginMarket.getSecNav()
					/ beginMarket.getAdjFactor() - 1;
		}
		return profit;
	}
}
